using Engine.FileFormats3D;
using Engine.FileFormats3D.WR2TerrainLoader.Misc;
using Scenario_Editor;
using Scenario_Editor.RoadBuilder;
using Scenario_Editor.Tools;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Windows.Forms;
using WorldRacing2.Configuration;
using WorldRacing2.Configuration.GroundConfig;
using WorldRacing2.Configuration.Material;
using WorldRacing2.Scenario;
using WorldRacing2.Scenario.WR2TerrainLoader;
using WR2Streetformat.WR2TrafficFiles;
using static Scenario_Editor.ScenarioFile;

namespace WorldRacing2.Exporter
{
    internal partial class ExportWindow : Form
    {
        #region Private Fields

        private readonly WorldRacing2ConfigurationData wr2Configuration;
        private DirectoryInfo c11UCRootDirectory;

        private DirectoryInfo projectDirectory;

        private DirectoryInfo scenarioDirectory;

        private DirectoryInfo wrRootDirectory;

        #endregion Private Fields

        #region Public Constructors

        public ExportWindow(WorldRacing2ConfigurationData wr2Configuration)
        {
            InitializeComponent();
            this.wr2Configuration = wr2Configuration;
        }

        #endregion Public Constructors

        #region Private Delegates

        private delegate void SetTextCallback(string newText);

        #endregion Private Delegates

        #region Internal Enums

        internal enum GameName
        {
            Wr2,
            C11Undercover
        }

        #endregion Internal Enums

        #region Public Properties

        public GroundsFile GroundFile { get; private set; }
        public Dictionary<string, TexturePropertie> TextureProperties { get; private set; }
        public TextureToMaterialFile TextureToMaterialFile { get; private set; }

        #endregion Public Properties

        #region Private Properties

        private ScenarioFile ScenarioFile { get; set; }

        #endregion Private Properties

        #region Public Methods

        public void ExportToWR2Scenario(ScenarioFile scenarioFile, DirectoryInfo projectDirectory, ScenarioFile.ExportOptions exportOptions,
            Configuration.GroundConfig.GroundsFile groundFile, Configuration.Material.TextureToMaterialFile textureToMaterial)
        {
            //this.terrain.CreateShadows();
            bool exportObjects = false;

            if ((exportOptions & ExportOptions.ExportObjects) == ExportOptions.ExportObjects)
            {
                exportObjects = true;
                scenarioFile.DynObjectManager.CopyObjectsToFolder(projectDirectory.FullName);
                scenarioFile.ObjectManager.SceneryObjectManager.CopyObjectsToFolder(projectDirectory.FullName);
                scenarioFile.RoadManager.SceneryObjectManager.CopyObjectsToFolder(projectDirectory.FullName);
            }

            Mesh3dData[] meshes = new Mesh3dData[scenarioFile.Terrain.nSectorsX * ScenarioFile.Terrain.nSectorsY];

            for (int y = 0; y < scenarioFile.Terrain.nSectorsY; y++)
            {
                for (int x = 0; x < scenarioFile.Terrain.nSectorsX; x++)
                {
                    meshes[x + y * scenarioFile.Terrain.nSectorsX] = new Mesh3dData(scenarioFile.MaterialManager,
                        scenarioFile.Terrain.GetTerrainMesh(x, y), ScenarioFile.MaterialManager.Engine.addinManager, ScenarioFile.MaterialManager.Engine.addinManager.CurrentAddin);
                }
            }

            if ((ExportOptions.Update & exportOptions) == ExportOptions.Update)
            {
                throw new NotImplementedException("Update scenario is not implemented at the moment!");

                //SendExportMessage("\nUpdating Scenerie\n");
                //IExportScenario scenario = addinManagerScenarioEditor.CurrentAddin.UpdateScenario(projectDirectory.FullName, this.textureSetupWindow.GetGrounds(),
                //    this.textureSetupWindow.TextureProperties, meshes, this.terrain.nSectorsY, this.terrain.nSectorsX, this.DynObjectManager);

                //if (exportObjects)
                //{
                //    scenario.QadFile.ResetObjects();
                //    scenario.QadFile.AddNewObjects(this.dynObjectManager, objectProperties);
                //    scenario.QadFile.AddNewObjects(this.objectManager.SceneryObjectManager, objectProperties);
                //    scenario.QadFile.AddNewObjects(this.RoadManager.SceneryObjectManager, objectProperties);
                //    scenario.QadFile.UpdateBlockinfosForObjects();
                //}

                //scenario.Save(projectDirectory.FullName);
            }

            if ((exportOptions & ExportOptions.CreateNew) == ExportOptions.CreateNew)
            {
                this.richTextBox.AppendText("\nCreating new Scenerie\n");
                var scenario = Wr2Scenario.CreateNewScenery(projectDirectory.FullName, this.wr2Configuration.GroundsFile.GetGrounds(),
                    this.wr2Configuration.TextureToMaterialFile.TextureProperties,
                    meshes, scenarioFile.Terrain.nSectorsY, scenarioFile.Terrain.nSectorsX);

                if (exportObjects)
                {
                    scenario.QadFile.ResetObjects();
                    scenario.QadFile.AddNewObjects(scenarioFile.DynObjectManager, this.wr2Configuration.SceneryObjectPropertiesFile);
                    scenario.QadFile.AddNewObjects(scenarioFile.ObjectManager.SceneryObjectManager, this.wr2Configuration.SceneryObjectPropertiesFile);
                    scenario.QadFile.AddNewObjects(scenarioFile.RoadManager.SceneryObjectManager, this.wr2Configuration.SceneryObjectPropertiesFile);
                    scenario.QadFile.UpdateBlockinfosForObjects();
                }

                scenario.Save(projectDirectory.FullName);
                // AddinManagerScenarioEditor.CurrentAddin.Exporter.CreateNewScenery(projectDirectory.FullName,
                //    meshes, this.terrain.nSectorsY, this.terrain.nSectorsX);
            }

            // Dispose all terrain meshes.
            for (int y = 0; y < scenarioFile.Terrain.nSectorsY; y++)
            {
                for (int x = 0; x < scenarioFile.Terrain.nSectorsX; x++)
                {
                    meshes[x + y * scenarioFile.Terrain.nSectorsX].Dispose();
                }
            }
            meshes = null;
        }

        public void SetVariables(ScenarioFile scenarioFile, string WRRoot)
        {
            UpdateFoldernames(WRRoot);
            this.ScenarioFile = scenarioFile;
        }

        #endregion Public Methods

        #region Private Methods

        private void AppendTextToLog(string text)
        {
            // InvokeRequired required compares the thread ID of the
            // calling thread to the thread ID of the creating thread.
            // If these threads are different, it returns true.
            if (this.richTextBox.InvokeRequired)
            {
                SetTextCallback d = new SetTextCallback(AppendTextToLog);
                //this.richTextBox.Invoke(d.Invoke(d, new object[] { text });
                this.richTextBox.Invoke(d, new object[] { text });
            }
            else
            {
                this.richTextBox.AppendText(text);
            }
        }

        private void button_browseToWr2Folder_Click(object sender, EventArgs e)
        {
            this.scenarioDirectory = new DirectoryInfo(this.wrRootDirectory.FullName);
            if (this.scenarioDirectory.Exists)
            {
                this.folderBrowserDialog_WRRoot.SelectedPath = this.scenarioDirectory.FullName;
            }
            if (this.folderBrowserDialog_WRRoot.ShowDialog(this) == DialogResult.OK)
            {
                UpdateFoldernames(this.folderBrowserDialog_WRRoot.SelectedPath);
            }
        }

        private void button_NewScenery_Click(object sender, EventArgs e)
        {
            NewScenario newScenarioDialog = new NewScenario();
            if (newScenarioDialog.ShowDialog(this) == DialogResult.OK)
            {
                Directory.CreateDirectory(this.scenarioDirectory + "\\" + newScenarioDialog.GetScenarioName());
                UpdateFoldernames(this.wrRootDirectory.FullName);
            }
        }

        /// <summary>
        /// Start Export to World Racing 2.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void buttonExport_Click(object sender, EventArgs e)
        {
            Button button = (Button)sender;
            button.Enabled = false;

            // Set focus to the richtextbox to get the scrollbars to the coursor.
            this.richTextBox.ScrollToCaret();
            this.richTextBox.Focus();

            this.ScenarioFile.statusExportChanged += new ScenarioFile.StatusExportChanged(OnStatusExportChange);

            Wr2Scenario.exportStatus += new Wr2Scenario.ExportStatus(OnStatusExportChange);

            if (this.projectDirectory != null && this.projectDirectory.Exists)
            {
                Stopwatch sw = new Stopwatch();
                sw.Start();
                // Export Scenery
                if (this.checkBox_ExportScenario.Checked)
                {
                    ScenarioFile.ExportOptions exportOptions = (this.radioButton_CreateNew.Checked) ? ScenarioFile.ExportOptions.CreateNew : ScenarioFile.ExportOptions.Update;

                    if (this.checkBox_ExportObjects.Checked)
                        exportOptions = exportOptions | ScenarioFile.ExportOptions.ExportObjects;

                    ExportToWR2Scenario(this.ScenarioFile, this.projectDirectory, exportOptions, this.GroundFile, this.TextureToMaterialFile);
                }

                if (this.checkBox_CopyTextures.Checked)
                {
                    CopyUsedTextures();
                }

                // Export Tracks
                if (this.checkBox_ExportTracks.Checked)
                {
                    try
                    {
                        for (int i = 0; i < this.ScenarioFile.TrackManager.Tracks.Count; i++)
                        {
                            if (false)
                            {
                                // Export LWO-File
                                string filename = this.projectDirectory.FullName + "\\track_" + ((int)(i + 1)).ToString() + ".lwo";

                                try
                                {
                                    this.ScenarioFile.TrackManager.Tracks[i].ExportToLWO(filename, 0.10f);
                                    this.richTextBox.AppendText("\nSaved to LWO file: \"" + filename + "\"\n");
                                }
                                catch
                                {
                                    this.richTextBox.AppendText(string.Format("\nError while writing Track_{0}", i));
                                }
                            }
                            else
                            {
                                //loopState.
                                string filename = this.projectDirectory.FullName + "\\V1\\Tracks\\" + this.projectDirectory.Name + "_" + ((int)(i + 1)).ToString().PadLeft(2, '0') + ".trk";
                                this.ScenarioFile.TrackManager.Tracks[i].ExportToWr2Track(filename, (ITerrain)ScenarioFile.Terrain, this.checkBox_CreateIdealline.Checked);

                                Application.DoEvents();
                                AppendTextToLog("\nTrack Saved: \"" + filename + "\"" /*+ ((float)sw.Elapsed.TotalSeconds).ToString() */+ "\n");
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show("Error track writing...");
                    }
                }

                if (this.checkBox_traffic.Checked)
                {
                    CreateTrafficFile(this.ScenarioFile.RoadManager, this.wrRootDirectory + "\\Traffic\\Streets\\" + this.projectDirectory.Name + ".str");
                }
                sw.Stop();
                try
                {
                    this.richTextBox.AppendText("\n\n" + ((float)sw.Elapsed.TotalSeconds).ToString() + " seconds elapsed\n\n");
                }
                catch
                {
                }
            }
            else
            {
                MessageBox.Show("Please select a Scenery Folder");
            }

            // Remove Update Events
            this.ScenarioFile.statusExportChanged -= OnStatusExportChange;
            Wr2Scenario.exportStatus -= OnStatusExportChange;
            button.Enabled = true;
            if (this.projectDirectory != null && this.projectDirectory.Exists)
            {
                StKit2Workfile.Export(this.projectDirectory.FullName + "\\V1\\" + this.projectDirectory.Name + " WorkFile.wrk", this.wr2Configuration.TextureToMaterialFile.TextureProperties, this.ScenarioFile.MaterialManager, QADFile.FromFile(this.projectDirectory.FullName + "\\V1\\" + this.projectDirectory.Name + ".qad"));
            }
        }

        private void checkBox_ExportTracks_CheckedChanged(object sender, EventArgs e)
        {
            this.checkBox_CreateIdealline.Enabled = (this.checkBox_ExportTracks.Checked) ? true : false;
        }

        private void comboBox_listedSceberies_SelectedIndexChanged(object sender, EventArgs e)
        {
            this.projectDirectory = new DirectoryInfo(this.scenarioDirectory.FullName + "\\" + this.comboBox_listedSceneries.SelectedItem.ToString());
        }

        private void CopyUsedTextures()
        {
            if (this.ScenarioFile.MaterialManager.GetNumberOfMaterials() > 0)
            {
                List<string> textureNames = new List<string>();
                for (int i = 0; i < this.ScenarioFile.MaterialManager.GetNumberOfMaterials(); i++)
                {
                    Engine.Graphics.Material item = this.ScenarioFile.MaterialManager.GetMaterial(i);

                    var textureNamesMaterial = item.GameMaterialProperties.GetUsedTextureNames();

                    foreach (string textureName in textureNamesMaterial)
                    {
                        if (textureName != string.Empty)
                            if (!textureNames.Contains(textureName))
                                textureNames.Add(textureName);
                    }

                    //if (!textureNames.Contains(item.TextureName1))
                    //    textureNames.Add(item.TextureName1);
                    //if (!textureNames.Contains(item.TextureName2))
                    //    textureNames.Add(item.TextureName2);
                    //if (!textureNames.Contains(item.TextureName3))
                    //    textureNames.Add(item.TextureName3);
                }

                foreach (string surfacename in textureNames)
                {
                    if (!Directory.Exists(this.projectDirectory + "\\V1\\Textures\\"))
                    {
                        Directory.CreateDirectory(this.projectDirectory + "\\V1\\Textures\\");
                    }
                    File.Copy(Application.StartupPath + "\\Texture\\" + surfacename + ".ptx", this.projectDirectory + "\\V1\\Textures\\" + surfacename + ".ptx", true);
                }
            }
        }

        private void CreateTrafficFile(RoadManager roadMesh, string filename)
        {
            WR2TrafficFile wr2TrafficFile = new WR2TrafficFile(roadMesh);
            wr2TrafficFile.Save(filename);
        }

        private void OnStatusExportChange(object sender, EventArgs e)
        {
            this.richTextBox.AppendText((string)sender);
        }

        private void UpdateFoldernames(string WRRootDirectory)
        {
            this.wrRootDirectory = new DirectoryInfo(WRRootDirectory);
            this.textBox_WRroot.Text = this.wrRootDirectory.FullName;
            this.scenarioDirectory = new DirectoryInfo(this.wrRootDirectory.FullName + "\\Scenarios");

            if (this.scenarioDirectory.Exists)
            {
                this.button_NewScenery.Enabled = true;
                DirectoryInfo[] directoriesScenarios = this.scenarioDirectory.GetDirectories();
                this.comboBox_listedSceneries.Items.Clear();
                foreach (DirectoryInfo dir in directoriesScenarios)
                {
                    this.comboBox_listedSceneries.Items.Add(dir.Name);
                }
            }
            else
            {
                this.projectDirectory = null;
                this.comboBox_listedSceneries.Items.Clear();
                this.comboBox_listedSceneries.Text = string.Empty;
                this.button_NewScenery.Enabled = false;
            }
        }

        #endregion Private Methods
    }
}